﻿using Common.Framework.Data;
using Common.Framework.Extensions;
using Common.Framework.Logger;
using System;
using System.Collections;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace DBUtil.Builders
{
    public class DeleteBuilder : WhereBuilder
    {
        protected static readonly ILogger<DeleteBuilder> logger = LoggerFactory.CreateLogger<DeleteBuilder>();
        public EnumDeleteBuilderType Type { get; internal set; }
        public string TableName { get; private set; }
        /// <summary>
        /// 主键值
        /// </summary>
        public object Object { get; set; }
        public DeleteBuilder(DBAccess db, string tableName, object obj) : base(db)
        {
            Type = EnumDeleteBuilderType.Delete;
            Object = obj;
            TableName = tableName;
        }

        #region 复写 AsTable
        public virtual DeleteBuilder AsTable(string newTableName)
        {
            TableName = newTableName;
            return this;
        }
        public virtual DeleteBuilder AsTableIf(bool condition, string newTableName)
            => condition ? AsTable(newTableName) : this;
        public virtual DeleteBuilder AsTable(Func<string, string> func)
        {
            var newTableName = func?.Invoke(TableName);
            if (newTableName.IsNotNullOrEmptyOrWhiteSpace()) TableName = newTableName;
            return this;
        }
        public virtual DeleteBuilder AsTableIf(bool condition, Func<string, string> func)
            => condition ? AsTable(func) : this;
        #endregion
        #region 复写 WhereSeg
        public override DeleteBuilder WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder;
        public override DeleteBuilder WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder;
        public override DeleteBuilder WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder;
        public override DeleteBuilder WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder;
        public override DeleteBuilder WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder;
        public override DeleteBuilder WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder;
        public override DeleteBuilder WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder;
        public override DeleteBuilder WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder;
        #endregion
        #region  复写 Where
        public override DeleteBuilder Where(string filter)
            => base.Where(filter) as DeleteBuilder;
        public override DeleteBuilder WhereIf(bool condition, string filter)
            => base.WhereIf(condition, filter) as DeleteBuilder;
        #endregion
        #region 复写 CommandTimeout
        public override DeleteBuilder CommandTimeout(int timeoutSeconds)
            => base.CommandTimeout(timeoutSeconds) as DeleteBuilder;
        public override DeleteBuilder CommandTimeoutIf(bool condition, int timeoutSeconds)
            => condition ? CommandTimeout(timeoutSeconds) : this;
        #endregion

        #region ToSql ExecuteAffrows(Async)
        /// <summary>
        /// 返回生成的sql
        /// </summary>
        public virtual string ToSql()
        {
            //先校验
            if (Type == EnumDeleteBuilderType.Delete && Filters.IsNullOrEmpty()) throw new Exception("不允许执行无 where 条件的delete语句!");
            var sb = new StringBuilder();
            sb.Append("delete from ").Append(TableName);
            //处理WhereSeg Where
            if (Filters.IsNotNullOrEmpty())
            {
                var filterSql = this.DealFilter(Filters);
                if (filterSql.IsNotNullOrEmpty()) sb.Append(" where ").Append(filterSql);
            }
            sb.Append(';');
            var sql = sb.ToString();
            sb.Clear();
            return sql;
        }

        public virtual int ExecuteAffrows()
        {
            var sql = ToSql();
            return RunWriteMonitor(new AfterWriteArgument
            {
                TableName = TableName,
                WriteType = EnumWriteType.Delete
            }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
        }
        public virtual async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
        {
            var sql = ToSql();
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                TableName = TableName,
                WriteType = EnumWriteType.Delete
            }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
        #endregion
    }

    /// <summary>
    /// 删除构造器
    /// </summary>
    public class DeleteBuilder<T> : DeleteBuilder where T : class, new()
    {
        protected static new readonly ILogger<DeleteBuilder<T>> logger = LoggerFactory.CreateLogger<DeleteBuilder<T>>();
        private EntityInfo EntityInfo { get; set; }
        #region 初始化
        public DeleteBuilder(DBAccess db, EnumDeleteBuilderType type, object obj) : base(db, db.GetEntityInfoInternal<T>().TableNameSeg, obj)
        {
            this.Type = type;
            this.EntityInfo = db.GetEntityInfoInternal<T>(false);
            if (type == EnumDeleteBuilderType.DeleteByPrimary && obj == null) throw new Exception("必须设置主键值!");
        }
        #endregion
        #region 复写 AsTable
        public override DeleteBuilder<T> AsTable(string newTableName)
        {
            base.AsTable(newTableName);
            return this;
        }
        public override DeleteBuilder<T> AsTableIf(bool condition, string newTableName)
            => condition ? AsTable(newTableName) : this;
        public override DeleteBuilder<T> AsTable(Func<string, string> func)
            => base.AsTable(func) as DeleteBuilder<T>;
        public override DeleteBuilder<T> AsTableIf(bool condition, Func<string, string> func)
            => condition ? AsTable(func) : this;
        #endregion
        #region 复写 WhereSeg
        public override DeleteBuilder<T> WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
            => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
            => base.WhereSeg(filter) as DeleteBuilder<T>;
        #endregion
        #region  复写 Where
        public override DeleteBuilder<T> Where(string filter)
            => base.Where(filter) as DeleteBuilder<T>;
        public override DeleteBuilder<T> WhereIf(bool condition, string filter)
            => base.WhereIf(condition, filter) as DeleteBuilder<T>;
        #endregion
        #region 复写 CommandTimeout
        public override DeleteBuilder<T> CommandTimeout(int timeoutSeconds)
            => base.CommandTimeout(timeoutSeconds) as DeleteBuilder<T>;
        public override DeleteBuilder<T> CommandTimeoutIf(bool condition, int timeoutSeconds)
            => condition ? CommandTimeout(timeoutSeconds) : this;
        #endregion
        #region Where
        public DeleteBuilder<T> Where(Expression<Func<T, bool>> filter)
        {
            Filters.Add(filter);
            return this;
        }
        public DeleteBuilder<T> WhereIf(bool condition, Expression<Func<T, bool>> filter)
        {
            if (!condition) return this;
            return Where(filter);
        }
        #endregion

        #region ToSql ExecuteAffrows(Async)
        public override string ToSql()
        {
            //先校验
            if (Type == EnumDeleteBuilderType.Delete) return base.ToSql();
            var sb = new StringBuilder();
            sb.Append("delete from ").Append(TableName);
            var hasWhere = false;

            //先处理按主键删除
            if (Type == EnumDeleteBuilderType.DeleteByPrimary)
            {
                var primaryMulti = EntityInfo.EntityPropertyInfos.Where(i => i.IsPrimaryKey && i.IsColumn).OrderBy(i => i.Order).ToList();
                var primary = EntityInfo.PrimaryKeyColumn;
                if (primary == null)
                    throw new Exception($"必须声明主键列({EntityInfo.TypeClassFullName}),否则无法使用 DeleteByPrimary！");

                var seg = db.ConvertToSqlSeg(Object).UnWrap();
                var cols = primary.ColumnNameSeg;
                if (primaryMulti.Count > 1) cols = "(" + primaryMulti.Select(i => i.ColumnNameSeg).ToStringSeparated(",") + ")";
                sb.Append(" where ").Append(cols).Append(Object is IEnumerable ? " in " : " = ").Append(seg);
                hasWhere = true;
            }
            //处理WhereSeg Where
            if (Filters.IsNotNullOrEmpty())
            {
                var filterSql = this.DealFilter(Filters);
                if (filterSql.IsNotNullOrEmpty())
                {
                    if (hasWhere) sb.Append(" and ").Append('(').Append(filterSql).Append(')');
                    else sb.Append(" where ").Append(filterSql);
                }
            }
            sb.Append(';');
            var sql = sb.ToString();
            sb.Clear();
            return sql;
        }

        public override int ExecuteAffrows()
        {
            var sql = ToSql();
            return RunWriteMonitor(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName,
                WriteType = EnumWriteType.Delete
            }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
        }
        public override async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
        {
            var sql = ToSql();
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName,
                WriteType = EnumWriteType.Delete
            }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
        #endregion
    }
}